<!DOCTYPE html>
<html>
<body>
  <h1>TTS文本转语音</h1>
  <div id="feedbackStats" style="margin-bottom: 10px; color: #666;">
    👍 点赞: <span id="likesCount">0</span> | 👎 点踩: <span id="dislikesCount">0</span>
  </div>
  <div style="margin-bottom: 20px;">
    <label for="textInput">输入文本:</label><br>
    <textarea id="textInput" rows="10" cols="80" placeholder="请输入要转换的文本...">以下是最新公告：

1. **湖南大学2026届校园招聘安排及生源信息**
   - <a href="https://scc.hnu.edu.cn/detail/news?id=1506999" target="_blank" rel="noopener noreferrer">查看详情</a>

此公告为最新的校园招聘信息，包含了湖南大学2026届学生的相关招聘安排和生源信息。企业用户可以点击链接查看详细内容，以便更好地了解和规划招聘活动。</textarea>
  </div>
  <div style="margin-bottom: 20px;">
    <label for="voiceSelect">选择语音:</label>
    <select id="voiceSelect">
      <option value="zf_001">女声001</option>
      <option value="zf_002">女声002</option>
      <option value="zm_010">男声010</option>
    </select>
  </div>
  <div style="margin-bottom: 20px;">
    <label for="speedInput">语速 (0.5-2.0):</label>
<input type="number" id="speedInput" min="0.5" max="2.0" step="0.1" value="1.0">
    </div>
   <button id="submitBtn">生成并播放音频</button>
   <button id="pauseBtn" disabled>暂停</button>
   <div id="feedbackSection" style="margin-top: 20px; display: none;">
     <h3>您对此结果满意吗？</h3>
     <button id="likeBtn" style="margin-right: 10px;">👍 点赞</button>
     <button id="dislikeBtn" style="margin-right: 10px;">👎 点踩</button>
     <input type="text" id="feedbackContent" placeholder="请输入您的反馈意见（可选）" style="margin-left: 10px; width: 300px;">
     <p id="feedbackMessage" style="margin-top: 10px; color: green;"></p>
   </div>

   <script>
    let mediaSource;
    let sourceBuffer;
      let audioPlayer;
      let isPlaying = false;
    // 初始化播放器
    function initPlayer() {
      // 重置所有相关变量
      if (mediaSource) {
        if (mediaSource.readyState !== 'closed') {
          mediaSource.endOfStream();
        }
        mediaSource = null;
      }
      sourceBuffer = null;
      if (audioPlayer) {
        audioPlayer.pause();
        audioPlayer.src = '';
      }
      isPlaying = false;

      // 创建新的媒体源和音频元素
      mediaSource = new MediaSource();
      audioPlayer = document.createElement('audio');
      audioPlayer.src = URL.createObjectURL(mediaSource);
      mediaSource.addEventListener('sourceopen', handleSourceOpen);

      // 更新按钮状态
      document.getElementById('submitBtn').disabled = false;
      document.getElementById('pauseBtn').disabled = true;
    }

    // MediaSource就绪后创建缓冲区
    function handleSourceOpen() {
      // 音频格式需与后端一致
      // 根据之前的修改，后端使用WAV格式，这里设置相应的MIME类型
      const mimeType = 'audio/wav; codecs="1"'; // PCM编码的WAV流，codecs="1"表示16位线性PCM
      // 检查浏览器是否支持该格式
      if (!MediaSource.isTypeSupported(mimeType)) {
        console.error('不支持的音频格式：', mimeType);
        alert('您的浏览器不支持WAV格式的流式播放，请尝试使用其他浏览器');
        return;
      }

      if (!MediaSource.isTypeSupported(mimeType)) {
        console.error('不支持的音频格式：', mimeType);
        return;
      }

      // 创建源缓冲区
      sourceBuffer = mediaSource.addSourceBuffer(mimeType);
      // 缓冲区数据处理完成后继续读取下一段
      sourceBuffer.addEventListener('updateend', fetchNextChunk);
    }

    // 从后端fetch流式数据并填充缓冲区
    async function fetchStream() {
      try {
        // 发起流式请求（后端需支持分块传输，如返回Transfer-Encoding: chunked）
        const response = await fetch('/stream-audio', {
          method: 'POST',
          headers: { 'Accept': 'audio/wav' } // 与后端格式匹配
        });

        if (!response.ok) throw new Error('请求失败');
        if (!response.body) throw new Error('后端不支持流式传输');

        // 获取可读流并逐段处理
        const reader = response.body.getReader();
        const processChunk = async ({ done, value }) => {
          if (done) {
            // 所有数据处理完成，标记流结束
            if (mediaSource.readyState === 'open') {
              mediaSource.endOfStream();
            }
            return;
          }

          // 若缓冲区未在更新，添加新数据
          if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
            sourceBuffer.appendBuffer(value); // 填充音频片段
          } else {
            // 缓冲区正忙，等待后重试
            setTimeout(() => processChunk({ done, value }), 10);
          }

          // 继续读取下一段
          return reader.read().then(processChunk);
        };

        // 开始读取流
        return reader.read().then(processChunk);
      } catch (err) {
        console.error('流式请求错误：', err);
      }
    }

    // 继续读取下一段（配合updateend事件）
    function fetchNextChunk() {
      // 若播放中且缓冲区需要更多数据，继续读取（此处逻辑需结合实际流控制）
      if (isPlaying && mediaSource.readyState === 'open') {
        // 实际场景中可根据缓冲区水位判断是否需要继续请求
      }
    }

    // 绑定按钮事件
// 生成唯一ID用于标识本次请求
function generateTextId() {
  return Math.random().toString(36).substr(2, 9) + Date.now();
}

// 当前文本ID
let currentTextId = null;

// 获取IP地址（简化版本，实际项目中可以使用更准确的方法）
async function getIpAddress() {
  try {
    const response = await fetch('https://api.ipify.org?format=json');
    const data = await response.json();
    return data.ip;
  } catch (error) {
    console.error('获取IP失败:', error);
    return 'unknown';
  }
}

// 提交访问数据
async function submitAccessLog(text, voice, speed) {
  try {
    const ip = await getIpAddress();
    const response = await fetch('http://localhost:8000/api/access-log', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        ip: ip,
        text: text,
        voice: voice,
        speed: speed
      })
    });
    const data = await response.json();
    console.log('访问数据提交结果:', data);
  } catch (error) {
    console.error('提交访问数据失败:', error);
  }
}

// 获取并显示反馈统计
async function fetchFeedbackStats() {
  try {
    const response = await fetch('http://localhost:8000/api/feedback/stats');
    const data = await response.json();
    if (data.status === 'success') {
      document.getElementById('likesCount').textContent = data.data.likes;
      document.getElementById('dislikesCount').textContent = data.data.dislikes;
    }
  } catch (error) {
    console.error('获取反馈统计失败:', error);
  }
}

// 提交点赞/点踩反馈
async function submitFeedback(rating) {
  try {
    const content = document.getElementById('feedbackContent').value;
    const response = await fetch('http://localhost:8000/api/feedback', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        text_id: currentTextId,
        rating: rating,
        content: content
      })
    });
    const data = await response.json();
    if (data.status === 'success') {
      document.getElementById('feedbackMessage').textContent = '感谢您的反馈！';
      document.getElementById('feedbackMessage').style.color = 'green';
      // 隐藏反馈按钮
      document.getElementById('likeBtn').disabled = true;
      document.getElementById('dislikeBtn').disabled = true;
      // 更新统计数据
      fetchFeedbackStats();
    }
  } catch (error) {
    console.error('提交反馈失败:', error);
    document.getElementById('feedbackMessage').textContent = '提交失败，请稍后重试';
    document.getElementById('feedbackMessage').style.color = 'red';
  }
}

document.getElementById('submitBtn').addEventListener('click', async () => {
  const text = document.getElementById('textInput').value;
  const voice = document.getElementById('voiceSelect').value;
  const speed = parseFloat(document.getElementById('speedInput').value);

  if (!text.trim()) {
    alert('请输入文本内容');
    return;
  }

  try {
    // 停止当前播放
    if (audioPlayer) {
      audioPlayer.pause();
      audioPlayer.src = '';
    }

    // 初始化播放器
    initPlayer();

    // 生成当前文本ID
    currentTextId = generateTextId();
    
    // 提交访问数据
    submitAccessLog(text, voice, speed);

    // 隐藏反馈区域和消息
    document.getElementById('feedbackSection').style.display = 'none';
    document.getElementById('feedbackMessage').textContent = '';
    document.getElementById('feedbackContent').value = '';

    // 发起流式请求
    const response = await fetch('http://localhost:8000/api/tts/stream', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        text: text,
        voice: voice,
        speed: speed
      })
    });

    if (!response.ok) throw new Error(`请求失败: ${response.status}`);
    if (!response.body) throw new Error('后端不支持流式传输');

    // 获取可读流并逐段处理
    const reader = response.body.getReader();
    const processChunk = async ({ done, value }) => {
      if (done) {
        // 所有数据处理完成，标记流结束
        if (mediaSource.readyState === 'open') {
          mediaSource.endOfStream();
        }
        return;
      }

      // 若缓冲区未在更新，添加新数据
      if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
        sourceBuffer.appendBuffer(value); // 填充音频片段
      } else {
        // 缓冲区正忙，等待后重试
        setTimeout(() => processChunk({ done, value }), 10);
      }

      // 继续读取下一段
      return reader.read().then(processChunk);
    };

    // 开始读取流
    reader.read().then(processChunk);

    // 尝试播放
    audioPlayer.play().then(() => {
      isPlaying = true;
      document.getElementById('submitBtn').disabled = true;
      document.getElementById('pauseBtn').disabled = false;
    }).catch(e => {
      console.error('播放失败:', e);
      alert('播放失败，请稍后重试');
    });

    // 监听播放结束事件，显示反馈按钮
    audioPlayer.addEventListener('ended', () => {
      document.getElementById('feedbackSection').style.display = 'block';
      document.getElementById('likeBtn').disabled = false;
      document.getElementById('dislikeBtn').disabled = false;
    });

  } catch (err) {
    console.error('请求错误:', err);
    alert(`请求错误: ${err.message}`);
  }
});

document.getElementById('pauseBtn').addEventListener('click', () => {
    if (isPlaying) {
      audioPlayer.pause();
      isPlaying = false;
      document.getElementById('submitBtn').disabled = false;
      document.getElementById('pauseBtn').disabled = true;
    } else {
      audioPlayer.play();
      isPlaying = true;
      document.getElementById('submitBtn').disabled = true;
      document.getElementById('pauseBtn').disabled = false;
    }
  });
  // 点赞按钮事件
  document.getElementById('likeBtn').addEventListener('click', () => {
    submitFeedback(1);
  });

  // 点踩按钮事件
  document.getElementById('dislikeBtn').addEventListener('click', () => {
    submitFeedback(-1);
  });

  // 页面加载时获取反馈统计
  fetchFeedbackStats();
  </script>
</body>
</html>